#include "HttpContext.h"
#include "HttpRequest.h"
#include "HttpResponse.h"
#include "Log.h"
#include <HttpServer.h>
#include <Util.h>
#include <regex>
#include <sstream>
#include <string>
#include <utility>

namespace muduo{
    // ====================================HttpServer-private==============================
    void HttpServer::ErrorHandler(HttpRequest& request,HttpResponse *response){
        // 组织一个错误页面填充到response中
        std::string body;
        body += "<html>";
        body += "<head>";
        body += "<meta http-equiv='Content-Type' content='text/html;charset=utf-8'>";
        body += "</head>";
        body += "<body>";
        body += "<h1>";
        body += std::to_string(response->_statu);
        body += " ";
        body += Util::StatuDesc(response->_statu);
        body += "</h1>";
        body += "</body>";
        body += "</html>";
        response->SetContent(body, "text/html");
    }
    void HttpServer::WriteResponse(const PtrConnection con,const HttpRequest& request,HttpResponse *response){
        // HttpResponse中按照http协议格式进行组织，发送
        // 1. 完善头部字段
        if(request.Close()==true){
            response->SetHeader("Connection", "close");
        }else{
            response->SetHeader("Connection", "keep-alive");
        }
        if(response->_body.empty()==false && response->HasHeader("Content-Length")==false){
            response->SetHeader("Content-Length", std::to_string(response->_body.size()));
        }
        if(response->_body.empty()==false && response->HasHeader("Content-Type")==false){
            response->SetHeader("Content-Type", "application/octet-stream");
        }
        if(response->_redirect_flag==true){
            response->SetHeader("Location", response->_redirect_url);
        }
        // 2. 根据response中的属性内容，按照http格式进行组织
        // stringstream是一个字符串流，属于流类，可以通过<< >> 流操作符对string进行写入与读取
        std::stringstream rsp_str;
        // http中response格式:version 状态码 状态码描述\r\n
        rsp_str<<request._version << " " << std::to_string(response->_statu) << " " << Util::StatuDesc(response->_statu)<<"\r\n";
        for(auto head : response->_header){
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        //头部完了之后还有个空行
        rsp_str <<"\r\n";
        rsp_str << response->_body;
        // 3. 将消息发送给客户端
        con->Send(rsp_str.str().c_str(), rsp_str.str().size());
    }
    bool HttpServer::IsFileHandler(const HttpRequest& request){
        // 1.必须存在静态资源根目录
        if(_base_dir.empty()==true){
            return false;
        }
        // 2.必须GET/HEAD请求方法
        if(request._method != "GET" && request._method!="HEAD"){
            return false;
        }
        // 3.请求的资源路径必须是一个合法的路径
        if(Util::ValidPath(request._path)==false){
            return false;
        }
        // 4.请求的资源必须存在，并且是一个普通文件
        std::string request_path = request._path;
        request_path+=_base_dir;
        if(request_path.back()=='/'){
            request_path+="index.html";
        }
        if(Util::IsRegular(request_path)==false){
            return false;
        }
        // 有一种特殊的请求是根目录/,这种情况需要在后面加上index.html
        return true;
    }
    // 静态资源请求：将静态文件的数据读取出来，并且设置mime
    void HttpServer::FileHandler(const HttpRequest& request,HttpResponse* response){
        // 1.获得完整的请求路径
        std::string request_path = request._path;
        request_path+=_base_dir;
        if(request_path.back()=='/'){
            request_path+="index.html";
        }
        // 2。根据请求路径获取资源
        bool ret = Util::ReadFile(request_path, &response->_body);
        if(ret == false) return;
        // 3.给response设置mime的header
        std::string mimeval = Util::Getmime(request_path);
        response->SetHeader("Content-Type", mimeval);
    }
    // 功能性请求处理
    void HttpServer::Dispatcher(HttpRequest& request,HttpResponse *response,const Handlers& handlers){
        for(auto &handle:handlers){
            // 取别名是为了防止每次都创建一个正则表达式对象
            const std::regex &e = handle.first;
            const Handler functor = handle.second;
            // 将请求的路径与路由表中存储的正则表达式进行匹配，结果放入req的match中
            bool ret = std::regex_match(request._path,request._matches,e);
            if(ret == false){
                continue;
            }
            return functor(request,response);
            
        }
        // 说明没有匹配到，那就直接返回错误码
        response->_statu=404;
    }
    void HttpServer::Route(HttpRequest& request,HttpResponse *response){
        // 1.对请求进行分辨，是静态请求还是功能性请求
        // 如果是静态请求，则进行静态资源处理，功能性请求则进行路由表确定是否有处理函数
        // 如果都不是，则返回405
        debuglog<<"in route";
        if(IsFileHandler(request) == true){
            FileHandler(request,response);
        }
        // 功能性请求
        if(request._method == "GET" || request._method == "HEAD"){
            return Dispatcher(request,response,_get_routine);
        }
        if(request._method == "POST"){
            debuglog<<"in post";
            return Dispatcher(request,response,_post_routine);
        }
        if(request._method == "PUT"){
            return Dispatcher(request,response,_put_routine);
        }
        if(request._method == "DELETE"){
            return Dispatcher(request,response,_delete_routine);
        }
        response->_statu=405;
    }
    void HttpServer::OnConnected(const PtrConnection con){
        con->SetContext(HttpContext());
        debuglog<< "new connection: " << con.get();
    }
    
    void HttpServer::OnMessage(const PtrConnection con,Buffer* buffer){
        while(buffer->EnableReadSize()>0){
            // 1.获取上下文数据
            HttpContext* context = con->GetContext()->get<HttpContext>();
            // 2.解析上下文数据
            context->RecvHttpRequest(buffer);
            // 2.1 解析出错，直接返回出错，关闭连接
            // 2.2 解析正常，才开始处理 
            HttpRequest &request = context->Request();
            HttpResponse response(context->RespStatu());
            if(context->RespStatu()>=400){
                ErrorHandler(request,&response);     //填充一个错误的页面放到response中
                WriteResponse(con,request,&response);    //组织响应发给客户端
                context->Reset();   //将上下文重置
                buffer->clear();    //缓冲区内容清空
                con->Shutdown();    //关闭连接
            }
            if(context->Recvstatu()!=RECV_HTTP_OVER){
                return; //返回等待新数据
            }
            //3.走到这里说明上下文接收完毕，则请求路由+业务处理
            Route(request,&response);
            //4.对HttpResponse进行组织发送
            WriteResponse(con ,request, &response);
            //5.重置上下文
            context->Reset();
            //6.根据长短连接判断是否需要继续处理
            if(response.Close()==true){
                con->Shutdown();
            }
        }
    }
    // ====================================HttpServer-public==============================
    HttpServer::HttpServer(int port,int timeout):_server(port){
        _server.SetInactiveRelease(timeout);
        _server.SetMessageCallback(bind(&HttpServer::OnMessage,this,std::placeholders::_1,std::placeholders::_2));
        _server.SetConnectedCallback(bind(&HttpServer::OnConnected,this,std::placeholders::_1));
    }
    void HttpServer::SetBaseDir(const std::string &basedir){_base_dir=basedir;}
    // 因为basedir是一个匹配规则字符串，需要转换为正则表达式对象放入路由表中进行后续的匹配
    void HttpServer::Get(const std::string &pattern, const Handler &handler){_get_routine.push_back(std::make_pair(std::regex(pattern), handler));}
    void HttpServer::Post(const std::string &pattern, const Handler &handler){_post_routine.push_back(std::make_pair(std::regex(pattern), handler));}
    void HttpServer::Put(const std::string &pattern, const Handler &handler){_put_routine.push_back(std::make_pair(std::regex(pattern), handler));}
    void HttpServer::Delete(const std::string &pattern, const Handler &handler){_delete_routine.push_back(std::make_pair(std::regex(pattern), handler));}
    void HttpServer::SetThreatPoolNum(int num){_server.SetPoolNum(num);}
    void HttpServer::Listen(){_server.start();}
}